﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FluentAssertions;
using Fixtures;
using Helpers;
using System.Data;
using Moq;

namespace DomainModel.Tests
{
    [TestClass]
    public class PatientTest
    {
        [TestMethod]
        public void Patient_IsInvalid_IfNameIsEmpty()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.Name = "";

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Name cannot be empty.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfNameIsNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.Name = null;

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Name cannot be empty.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfNameOnlyHasSpaces()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.Name = "   ";

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Name cannot be empty.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfNameHasMoreThan50Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.Name = "a".Times(51);

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Name cannot be more than 50 characters.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsValid_IfNameHas50Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.Name = "a".Times(50);

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(0);
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfPhoneNumberHasMoreThan30Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.PhoneNumber = "a".Times(31);

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Phone number cannot be more than 30 characters.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsValid_IfPhoneNumberHas30Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.PhoneNumber = "a".Times(30);

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsValid_IfPhoneNumberIsNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.PhoneNumber = null;

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfEmailAddressHasMoreThan30Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.EmailAddress = "a".Times(25) + "@a.com";

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Email address cannot be more than 30 characters.");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsValid_IfEmailAddressrHas30Characters()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.EmailAddress = "a".Times(24) + "@a.com";

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsValid_IfEmailAddressIsNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.EmailAddress = null;

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsValid_IfEmailAddressIsEmpty()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.EmailAddress = "";

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfEmailAddressIsNotInCorrectFormat()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.EmailAddress = "aaa";

            // Act
            var errors = patient.Errors;

            // Assert
            errors.Count.Should().Be(1);
            errors[0].Should().Be("Email address is invalid. Please make sure it is in the following format (For e.g. mano@mano.com).");
            patient.IsValid.Should().BeFalse();
        }

        [TestMethod]
        public void Patient_IsValid_IfDateOfBirthIsNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.DateOfBirth = null;

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfDateOfBirthIsInTheFuture()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.DateOfBirth = DateTime.Now.Date.AddDays(1);

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeFalse();
            patient.Errors.Count.Should().Be(1);
            patient.Errors[0].Should().Be("Date of birth cannot be in the future.");
        }

        [TestMethod]
        public void Patient_IsValid_IfDateOfBirthIsToday()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.DateOfBirth = DateTime.Now.Date;

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInvalid_IfDateOfBirthIsBefore1900()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.DateOfBirth = DateTime.Parse("1899-12-31");

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeFalse();
            patient.Errors.Count.Should().Be(1);
            patient.Errors[0].Should().Be("Date of birth cannot be before 01-01-1900. He should be really old!");
        }

        [TestMethod]
        public void Patient_IsValid_IfDateOfBirthIs1900_01_01()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.DateOfBirth = DateTime.Parse("1900-01-01");

            // Act
            var errors = patient.Errors;

            // Assert
            patient.IsValid.Should().BeTrue();
        }

        [TestMethod]
        public void Patient_IsInValid_IfHomeAddressIsInvalid()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.HomeAddress.Line1 = "a".Times(101);

            // Act
            patient.Errors.Count.Should().Be(1);
            patient.IsValid.Should().BeFalse();
            patient.Errors[0].Should().Be("Line 1 cannot be more than 100 characters.");
        }

        [TestMethod]
        public void Patient_IsInValid_IfHomeAddressIsNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();
            patient.HomeAddress = null;

            // Act
            patient.Errors.Count.Should().Be(1);
            patient.IsValid.Should().BeFalse();
            patient.Errors[0].Should().Be("Home address cannot be null.");
        }

        [TestMethod]
        public void PatientProperties_ShouldBeCorrectlyPopulatedFromReader()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();

            var readerMock = new Mock<IDataReader>();
            readerMock.Setup(x => x["Id"]).Returns(patient.Id);
            readerMock.Setup(x => x["Name"]).Returns(patient.Name);
            readerMock.Setup(x => x["MedicalRecordNumber"]).Returns(patient.MedicalRecordNumber);
            readerMock.Setup(x => x["Gender"]).Returns(patient.Gender.ToString());
            readerMock.Setup(x => x["DateOfBirth"]).Returns(patient.DateOfBirth);
            readerMock.Setup(x => x["EmailAddress"]).Returns(patient.EmailAddress);
            readerMock.Setup(x => x["PhoneNumber"]).Returns(patient.PhoneNumber);

            // Act
            var newPatient = new Patient(readerMock.Object);

            // Assert
            newPatient.Id.Should().Be(patient.Id);
            newPatient.Name.Should().Be(patient.Name);
            newPatient.MedicalRecordNumber.Should().Be(patient.MedicalRecordNumber);
            newPatient.Gender.Should().Be(patient.Gender);
            newPatient.DateOfBirth.Should().Be(patient.DateOfBirth);
            newPatient.EmailAddress.Should().Be(patient.EmailAddress);
            newPatient.PhoneNumber.Should().Be(patient.PhoneNumber);
        }

        [TestMethod]
        public void PatientProperties_ThatAreNullable_ShouldBeNull_IfReaderHasDBNull()
        {
            // Arrange
            var patient = PatientFixture.GetPatient();

            var readerMock = new Mock<IDataReader>();
            readerMock.Setup(x => x["Id"]).Returns(patient.Id);
            readerMock.Setup(x => x["Name"]).Returns(patient.Name);
            readerMock.Setup(x => x["MedicalRecordNumber"]).Returns(patient.MedicalRecordNumber);
            readerMock.Setup(x => x["Gender"]).Returns(patient.Gender.ToString());
            readerMock.Setup(x => x["DateOfBirth"]).Returns(DBNull.Value);
            readerMock.Setup(x => x["EmailAddress"]).Returns(DBNull.Value);
            readerMock.Setup(x => x["PhoneNumber"]).Returns(DBNull.Value);

            // Act
            var newPatient = new Patient(readerMock.Object);

            // Assert
            newPatient.Id.Should().Be(patient.Id);
            newPatient.Name.Should().Be(patient.Name);
            newPatient.MedicalRecordNumber.Should().Be(patient.MedicalRecordNumber);
            newPatient.Gender.Should().Be(patient.Gender);
            newPatient.DateOfBirth.Should().Be(null);
            newPatient.EmailAddress.Should().Be(null);
            newPatient.PhoneNumber.Should().Be(null);
        }
    }
}
